/******************************************************************************
 copyright (C), 2018, hisilicon tech. co., ltd.
******************************************************************************
file name     : pm.c
version       : initial draft
author        : hisilicon multimedia software group
created       : 2018/09/17
last modified :
description   :
function list :
******************************************************************************/

#include "hi_osal.h"
#include "hi_common.h"
#include "hi_type.h"
#include "mod_ext.h"
#include "hi_debug.h"
#include "proc_ext.h"
#include "sys_ext.h"


#include "dev_ext.h"
#include "pm.h"
#include "mkp_pm.h"

#include "pm_device.h"
#include "pm_dev_api.h"
#include "pm_ext.h"

#include "autoconf.h"

osal_spinlock_t g_pm_lock;
osal_semaphore_t g_pm_sem;

/*
#if (HICHIP==HI3516C_V500) || (HICHIP==HI3519A_V100)

#else

#endif
*/


pm_ctx                  *g_pm_ctx = HI_NULL;
static osal_dev_t          *ast_pm_device;
pm_mod_state         g_pm_mod_state = PM_MOD_STATE_STOPPED;  /*module state*/
static osal_atomic_t        s_pm_user_ref = OSAL_ATOMIC_INIT(0);
hi_mpi_pm_media_cfg  g_pm_param;

extern struct pm_device g_pm_dev;

int g_avsp_on = HI_FALSE;
int g_svp_accelerator_on = HI_FALSE;
/***********************************************************************************
 prototype      : pm_proc_show
 description  : PM proc information
 input          : struct seq_file* s, hi_void* data
 output       : none
 return value :
 calls          :
 called by      :

 history      :
 date          : 2018/09/17
 author       :
 modification : created function
***********************************************************************************/

#define _1M_ 1000000
hi_s32 pm_proc_show(osal_proc_entry_t *sfile)
{
    osal_seq_printf(sfile, "\n-------------------------------------------------------------------\n");
    osal_seq_printf(sfile, "PM status:\n");
    osal_seq_printf(sfile, "  domain_num    = %0d;\n", g_pm_dev.domain_num);
    osal_seq_printf(sfile, "  pm_enable     = %0d;\n", g_pm_dev.pm_enable);
    osal_seq_printf(sfile, "-------------CPU domain-------------\n");
#ifdef PM_DOMAIN_CPU_ON
    struct pm_domain_device *cpu = g_pm_dev.cpu_domain_device;
    struct freq_device *cpu_freq = cpu->freq_dev;
    if (cpu_freq != NULL) {
        osal_seq_printf(sfile, "  dvfs_enable   = %0d\n", cpu_freq->dvfs_enable);
        osal_seq_printf(sfile, "  cur_volt    = %0du_v \n", cpu->cur_volt);
    }
#else
    osal_seq_printf(sfile, "    not support  \n");
#endif
    osal_seq_printf(sfile, "------------- MEDIA domain -------------\n");
#ifdef PM_DOMAIN_MEDIA_ON
    struct pm_domain_device *media = g_pm_dev.media_domain_device;
    struct avs_device *media_avs = media->avs_dev;
    if (media_avs != NULL) {
        extern int hal_media_get_avs_policy(void);
        hi_media_avs_policy media_avs_policy = (hi_media_avs_policy)hal_media_get_avs_policy();
        if ((media_avs_policy != HI_MEDIA_AVS_POLICY_CLOSE) && (media_avs_policy != HI_MEDIA_AVS_POLICY_BUTT)) {
            osal_seq_printf(sfile, "  cur_volt    = %0du_v \n",  media->cur_volt);
        }
        osal_seq_printf(sfile, "  profile_num   = %0d;\n", media_avs->profile_num);
        osal_seq_printf(sfile, "  cur_profile   = %0d;\n", 0);
    }
#else
    osal_seq_printf(sfile, "    not support    \n");
#endif
    osal_seq_printf(sfile, "-------------CORE domain -------------\n");
#ifdef PM_DOMAIN_CORE_ON
    struct pm_domain_device *core = g_pm_dev.core_domain_device;
    struct avs_device *core_avs = core->avs_dev;
    if (core_avs != NULL) {
        osal_seq_printf(sfile, "  cur_volt    = %0d;\n", core->cur_volt);
    }
#else
    osal_seq_printf(sfile, "    not support  \n");
#endif
    osal_seq_printf(sfile, "\n-------------------------------------------------------------------\n");
    return 0;
}



hi_s32 pm_check_module_param(void)
{
    if (g_avsp_on > 1) {
        osal_printk("module_param:avsp_on =%d range[0,1]\n", g_avsp_on);
        return HI_FAILURE;
    }
    if (g_svp_accelerator_on > 1) {
        osal_printk("module_param:svp_accelerator_on =%d range[0,1]\n", g_svp_accelerator_on);
        return HI_FAILURE;
    }
    return HI_SUCCESS;
}

hi_s32     pm_init(hi_void *args)
{
    hi_s32     ret = -1;
    /*if mod has inserted , just return ok*/
    if (g_pm_mod_state == PM_MOD_STATE_STARTED) {
        return HI_SUCCESS;
    }
    if (pm_check_module_param() == HI_FAILURE) {
        HI_PRINT("PM module param is illegal!\n");
        return HI_FAILURE;
    }
#if NEED_MEM_INFO
    osal_printk("sizeof(pm_ctx):%d \n", sizeof(pm_ctx));
#endif
    g_pm_ctx = (pm_ctx *)osal_vmalloc(sizeof(pm_ctx));
    if (g_pm_ctx == HI_NULL) {
        HI_TRACE_PM(HI_DBG_ERR, "vmalloc for g_pm_ctx(size = %d)failed!!  \n", sizeof(pm_ctx));
        return ret;
    }
    /*init proc info*/
    osal_memset(&g_pm_ctx->s_pm_dbg_info, 0, sizeof(pm_dbg_info));
    /*change mod state as started*/
    g_pm_mod_state = PM_MOD_STATE_STARTED;
    return HI_SUCCESS;
}

hi_void  pm_exit(hi_void)
{
    if (g_pm_mod_state == PM_MOD_STATE_STOPPED) {
        return;
    }
    osal_vfree(g_pm_ctx);
    g_pm_ctx = HI_NULL;
    g_pm_mod_state = PM_MOD_STATE_STOPPED;
    HI_TRACE_PM(HI_DBG_INFO, "PM exit successfully...\n");
    return;
}

static hi_void pm_notify(mod_notice_id notice)
{
    if (notice == MOD_NOTICE_STOP) {
        HI_TRACE_PM(HI_DBG_INFO, "PM was notified to exit ...\n");
        if (g_pm_mod_state == PM_MOD_STATE_STOPPED) {
            return;
        }
        g_pm_mod_state = PM_MOD_STATE_STOPPING;
    }
    return;
}

static hi_void pm_query_state(mod_state *state)
{
    if (g_pm_mod_state == PM_MOD_STATE_STARTED) {
        *state =  MOD_STATE_BUSY ;
    } else {
        *state = MOD_STATE_FREE ;
    }
    return;
}

static hi_u32 pm_get_ver_magic(hi_void)
{
    return VERSION_MAGIC;
}

static long pm_ioctl(unsigned int cmd, unsigned long arg, void *private_data)
{
    hi_s32 ret = HI_SUCCESS;
    if (g_pm_mod_state != PM_MOD_STATE_STARTED) {
        return HI_ERR_PM_SYS_NOTREADY;
    }
    osal_atomic_inc_return(&s_pm_user_ref);
    switch (cmd) {
        case PM_SET_MEDIA_PARAM: {
            hi_mpi_pm_media_cfg *usr_param = (hi_mpi_pm_media_cfg *) arg;
            ret = set_media_profile_by_usr_cfg(usr_param);
            break;
        }
        case PM_GET_MEDIA_PARAM: {
            ret = get_media_usrcfg((hi_mpi_pm_media_cfg *) arg);
            break;
        }
        case PM_SET_SINGLE_MEDIA_PARAM: {
            hi_mpi_pm_sigle_media_cfg *usr_param = (hi_mpi_pm_sigle_media_cfg *) arg;
            ret = set_single_freq_by_usr_cfg(usr_param);
            break;
        }
        case PM_SVP_ENABLE_CTRL: {
            svp_ctrl(1);
            break;
        }
        case PM_DVFS_ENABLE_CTRL: {
            dvfs_ctrl(1);
            break;
        }
        case PM_DVFS_DISABLE_CTRL: {
            dvfs_ctrl(0);
            break;
        }
        case PM_GET_TEMPERATURE: {
            hi_s32 temperature = pm_hal_get_temperature();
            *(hi_s32 *)arg = temperature;
            break;
        }
        default:
            ret = HI_FAILURE;
            break;
    }
    osal_atomic_dec_return(&s_pm_user_ref);
    return ret;
}

#ifdef CONFIG_COMPAT
static long pm_compat_ioctl(unsigned int cmd, unsigned long arg, void *private_data)
{
    switch (cmd) {
        default: {
            break;
        }
    }
    return pm_ioctl(cmd, arg, private_data);
}
#endif

static int pm_open(void *private_data)
{
    return HI_SUCCESS;
}

static int pm_close(void *private_data)
{
    return HI_SUCCESS;
}

static int pm_mmap(osal_vm_t *vm, unsigned long start, unsigned long end, unsigned long vm_pgoff, void *private_data)
{
    return HI_SUCCESS;
}


static umap_module s_module = {
    .mod_id        = HI_ID_PM,
    .mod_name       = "pm",

    .pfn_init        = pm_init,
    .pfn_exit        = pm_exit,
    .pfn_query_state     = pm_query_state,
    .pfn_notify         = pm_notify,
    .pfn_ver_checker    = pm_get_ver_magic,
    .export_funcs = NULL,
    .data = HI_NULL,
};

static struct osal_fileops pm_fops = {
    .open            = pm_open,
    .unlocked_ioctl = pm_ioctl,
    .release        = pm_close,
    .mmap           = pm_mmap,
#ifdef CONFIG_COMPAT
    .compat_ioctl    = pm_compat_ioctl
#endif

};

static int pm_freeze(osal_dev_t *pdev)
{
    return HI_SUCCESS;
}

static int pm_restore(osal_dev_t *pdev)
{
    return HI_SUCCESS;
}

struct osal_pmops pm_drv_ops = {
    .pm_freeze  = pm_freeze,
    .pm_restore = pm_restore,
};


void pm_mod_init(void)
{
    char buf[20];
    osal_proc_entry_t *proc;
    osal_snprintf(buf, 20, "%s", UMAP_DEVNAME_PM_BASE);
    ast_pm_device = osal_createdev(buf);
    ast_pm_device->fops  = &pm_fops;
    ast_pm_device->minor = UMAP_PM_MINOR_BASE;
    ast_pm_device->osal_pmops = &pm_drv_ops;
    if (osal_registerdevice(ast_pm_device) < 0) {
        HI_TRACE_PM(HI_DBG_ERR, "regist device err.\n");
        return ;
    }
    if (cmpi_register_module(&s_module)) {
        osal_printk("load pm.ko for %s...FAILURE!\n", CHIP_NAME);
        goto fail;
    }
    pm_dev_init(false, false);
    proc = osal_create_proc_entry(PROC_ENTRY_PM, NULL);
    if (proc == NULL) {
        HI_TRACE_PM(HI_DBG_ERR, "PM create proc failed. \n");
        goto fail;
    }
    proc->read = pm_proc_show;
    if (osal_atomic_init(&s_pm_user_ref)) {
        HI_TRACE_PM(HI_DBG_ERR, "PM atomic init failed. \n");
        goto fail1;
    }
    osal_atomic_set(&s_pm_user_ref, 0);
    if (osal_sema_init(&g_pm_sem, 1)) {
        HI_TRACE_PM(HI_DBG_ERR, "pm sem init failed. \n");
        goto fail1;
    }
    osal_printk("load pm.ko for %s...OK!\n", CHIP_NAME);
    return;
fail1:
    osal_remove_proc_entry(PROC_ENTRY_PM, NULL);
fail:
    osal_deregisterdevice(ast_pm_device);
    osal_destroydev(ast_pm_device);
    ast_pm_device = HI_NULL;
    return ;
}

void pm_mod_exit(void)
{
    if (ast_pm_device != HI_NULL) {
        /* stop the pm dev */
        pm_dev_deinit();
        osal_sema_destory(&g_pm_sem);
        cmpi_unregister_module(HI_ID_PM);
        osal_remove_proc_entry(PROC_ENTRY_PM, NULL);
        osal_deregisterdevice(ast_pm_device);
        osal_destroydev(ast_pm_device);
        osal_atomic_destory(&s_pm_user_ref);
    }
    osal_printk("unload pm.ko for %s...OK!\n", CHIP_NAME);
    return ;
}

